package com.wmh.baseservice.common.wechat.pay.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxScanPayNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.wmh.baseservice.admin.pojo.AdInfo;
import com.wmh.baseservice.common.ex.ServiceException;
import com.wmh.baseservice.common.utils.CommUtil;
import com.wmh.baseservice.common.utils.UUIDUtil;
import com.wmh.baseservice.common.utils.returnbody.ResultCodeInfo;
import com.wmh.baseservice.common.utils.returnbody.ReturnBody;
import com.wmh.baseservice.merchant.pojo.MerchantInfo;
import com.wmh.baseservice.merchant.pojo.MerchantPlotRegion;
import com.wmh.baseservice.merchant.service.MerchantInfoService;
import com.wmh.baseservice.merchant.service.MerchantPlotRegionService;
import com.wmh.baseservice.system.pojo.SysParams;
import com.wmh.baseservice.system.service.SysParamsService;
import com.wmh.baseservice.user.pojo.*;
import com.wmh.baseservice.user.service.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;


/**
 * 微信支付示例代码
 *
 * @author Binary Wang
 */
@Api(tags = "微信支付")
@RestController
@RequestMapping(value = "wx/pay", produces = "text/plain;charset=utf-8")
public class WxPayController {

    @Resource
    private WxPayService wxPayService;
    @Resource
    private HttpServletRequest request;
    @Resource
    private UsInfoService usInfoService;
    @Resource
    private UsTopUpWithdrawalService usTopUpWithdrawalService;
    @Resource
    private UsGroupGoodsOrderService usGroupGoodsOrderService;
    @Resource
    private UsVegetableOrderService usVegetableOrderService;
    @Resource
    private SysParamsService sysParamsService;
    @Resource
    private MerchantInfoService merchantInfoService;
    @Resource
    private UsActivityRegisterService usActivityRegisterService;
    @Resource
    private UsRentOrderService usRentOrderService;
    @Resource
    private MerchantPlotRegionService merchantPlotRegionService;
    @Resource
    private UsParentOrderService usParentOrderService;


    @ApiOperation(value = "查询订单")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", dataTypeClass = String.class, name = "transactionId", value = "微信订单号"),
            @ApiImplicitParam(paramType = "query", dataTypeClass = String.class, name = "outTradeNo", value = "商户系统内部的订单号，当没提供transactionId时需要传这个"),
    })
    @PostMapping("/queryOrder")
    public String queryOrder(String transactionId, String outTradeNo) throws WxPayException {
        WxPayOrderQueryResult wxPayOrderQueryResult = this.wxPayService.queryOrder(transactionId, outTradeNo);
        return ReturnBody.success(wxPayOrderQueryResult);
    }

    @ApiOperation(value = "统一下单（JSAPI支付）")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", dataTypeClass = String.class, name = "orderNum", value = "订单号"),
            @ApiImplicitParam(paramType = "query", dataTypeClass = Integer.class, name = "type", value = "1微信充值 2拼团订单 3摘种订单 4商家缴纳保证金 5活动支付 6租赁订单 7购物车结算"),
    })
    @PostMapping("/createJsApiOrder")
    @RequiresRoles(logical = Logical.OR, value = {"merchant", "user"})
    public String createJsApiOrder(String orderNum, Integer type) {
        UsInfo usInfo = checkUs(orderNum);
        // 自定义参数，回调时原样返回
        JSONObject attach = new JSONObject(true);
        BigDecimal money = null;
        switch (type) {
            case 1:
                UsTopUpWithdrawal usTopUpWithdrawal = usTopUpWithdrawalService.getOneForUpdate(new QueryWrapper<UsTopUpWithdrawal>().eq(UsTopUpWithdrawal.ORDER_NUM, orderNum));
                if (!CommUtil.checkNull(usTopUpWithdrawal) || !usTopUpWithdrawal.getUsId().equals(usInfo.getId())) {
                    return ReturnBody.error(ResultCodeInfo.PARAM_ERROR);
                }
                money = usTopUpWithdrawal.getMoney();
                break;
            case 2:
                UsGroupGoodsOrder usGroupGoodsOrder = usGroupGoodsOrderService.getOneForUpdate(new QueryWrapper<UsGroupGoodsOrder>().eq(UsGroupGoodsOrder.ORDER_NO, orderNum));
                if (!CommUtil.checkNull(usGroupGoodsOrder) || !usGroupGoodsOrder.getUsId().equals(usInfo.getId())) {
                    return ReturnBody.error(ResultCodeInfo.PARAM_ERROR);
                }
                if (usGroupGoodsOrder.getStatus() != 0) {
                    return ReturnBody.error("订单状态错误");
                }
                money = usGroupGoodsOrder.getRealPrice();
                break;
            case 3:
                UsVegetableOrder usVegetableOrder = usVegetableOrderService.getOneForUpdate(new QueryWrapper<UsVegetableOrder>().eq(UsVegetableOrder.ORDER_NUM, orderNum));
                if (!CommUtil.checkNull(usVegetableOrder)) {
                    return ReturnBody.error(ResultCodeInfo.PARAM_ERROR);
                }
                if (usVegetableOrder.getStatus() != 0) {
                    return ReturnBody.error("订单状态错误");
                }
                money = usVegetableOrder.getTotalPrice();
                break;
            case 4:
                //充值的保证金都在主商家处
                SysParams sysParams = sysParamsService.getByIdForUpdate(Long.parseLong(orderNum.split(":")[0]));
                if (!CommUtil.checkNull(sysParams)) {
                    return ReturnBody.error(ResultCodeInfo.PARAM_ERROR);
                }
                money = new BigDecimal(sysParams.getValue());
                attach.put("merchantId", usInfo.getMerchantId());
                orderNum = UUIDUtil.getTimeId() + UUIDUtil.getLetter();
                break;
            case 5:
                // 活动支付 校验参数
                JSONObject result = usActivityRegisterService.wechatPaySignUpCommitCheck(orderNum);
                money = result.getBigDecimal("money");
                break;
            case 6:
                UsRentOrder usRentOrder = usRentOrderService.getOneForUpdate(new QueryWrapper<UsRentOrder>().eq(UsRentOrder.ORDER_NUM, orderNum));
                if (!CommUtil.checkNull(usRentOrder) || !usRentOrder.getUsId().equals(usInfo.getId())) {
                    return ReturnBody.error(ResultCodeInfo.PARAM_ERROR);
                }
                if (usRentOrder.getStatus() != 0) {
                    return ReturnBody.error("订单状态错误");
                }
                MerchantPlotRegion merchantPlotRegion = merchantPlotRegionService.getByIdForUpdate(usRentOrder.getRegionId());
                if (!CommUtil.checkNull(merchantPlotRegion) || merchantPlotRegion.getStatus() == 2 && !merchantPlotRegion.getMerchantId().equals(usInfo.getMerchantId())) {
                    return ReturnBody.error("该区域已租出");
                }
                money = usRentOrder.getPayAmount();
                break;
            case 7:
                final BigDecimal[] total = {BigDecimal.ZERO};
                UsParentOrder usParentOrder = usParentOrderService.getOneForUpdate(new QueryWrapper<UsParentOrder>().eq(UsParentOrder.ORDER_NUM, orderNum));
                JSONArray array = JSONArray.parseArray(usParentOrder.getSonOrder());
                array.forEach(item -> {
                    UsVegetableOrder order = usVegetableOrderService.getOneForUpdate(new QueryWrapper<UsVegetableOrder>().eq(UsVegetableOrder.ORDER_NUM, item));
                    if (!CommUtil.checkNull(order)) {
                        throw new ServiceException("订单不存在");
                    }
                    total[0] = total[0].add(order.getTotalPrice());
                });
                money = total[0];
                break;
            default:
                return ReturnBody.error(ResultCodeInfo.PARAM_ERROR);
        }
        // TODO 获取OPENID
        String openId = usInfo.getOpenId();
        attach.put("orderNum", orderNum);
        attach.put("type", type);
        // 支付金额，单位：分
        BigDecimal totalAmount = money.multiply(BigDecimal.valueOf(100));
        // 回调路径
        String url = request.getRequestURL().toString();
        url = url.substring(0, url.lastIndexOf("/"));
        WxPayUnifiedOrderRequest payRequest;
        try {
            payRequest = WxPayUnifiedOrderRequest.newBuilder().outTradeNo(orderNum)
                    .openid(openId).body("在线支付").totalFee(totalAmount.intValue())
                    .spbillCreateIp(InetAddress.getLocalHost().getHostAddress()).tradeType("JSAPI")
                    .productId("10000").notifyUrl("https://www.tdtye.com/api/wx/pay/public/notifyPay")
                    .attach(attach.toJSONString()).build();
            WxPayMpOrderResult order = this.wxPayService.createOrder(payRequest);
            order.setAppId(null);
            return ReturnBody.success(order);
        } catch (UnknownHostException | WxPayException e) {
            e.printStackTrace();
            return ReturnBody.error("统一下单失败");
        }
    }

    @ApiIgnore
    @ApiOperation(value = "支付回调通知处理")
    @PostMapping("/public/notifyPay")
    public String parseOrderNotifyResult(@RequestBody String xmlData) throws WxPayException {
        final WxPayOrderNotifyResult notifyResult = this.wxPayService.parseOrderNotifyResult(xmlData);
        // TODO 业务逻辑处理，注意幂等性验证
        JSONObject object = JSONObject.parseObject(notifyResult.getAttach());
        String orderNum = object.getString("orderNum");
        Integer type = object.getInteger("type");
        switch (type) {
            case 1:
                //充值
                usTopUpWithdrawalService.topUp(orderNum);
                break;
            case 2:
                //拼单
                usGroupGoodsOrderService.weChatPay(orderNum);
                break;
            case 3:
                //农场摘种
                usVegetableOrderService.weChatPay(orderNum);
                break;
            case 4:
                //缴纳保证金
                Long merchantId = object.getLong("merchantId");
                merchantInfoService.marginPay(merchantId);
                break;
            case 5:
                // 活动支付
                usActivityRegisterService.wechatPaySignUpComplete(orderNum);
                break;
            case 6:
                merchantPlotRegionService.wechatPay(orderNum);
                break;
            case 7:
                //果蔬购物车结算
                usVegetableOrderService.shopCarWeChatPay(orderNum);
                break;
            default:
                return ReturnBody.error(ResultCodeInfo.PARAM_ERROR);
        }
        return WxPayNotifyResponse.success("成功");
    }

    @ApiOperation(value = "会员充值")
    @PostMapping("us/memberRecharge/qrCode")
    public String memberRecharge(Double money) {
        Long usId = (Long) request.getAttribute(AdInfo.ID);
        JSONObject attach = new JSONObject();
        attach.put("usId", usId);
        attach.put("type", 1);
        String body = "会员充值";
        return createQrCodeOrder(money, attach.toJSONString(), body);
    }

    /**
     * 扫码支付
     *
     * @param money  金额
     * @param attach 自定义参数
     * @param body
     * @return
     */
    public String createQrCodeOrder(Double money, String attach, String body) {
        if (money < 0.01) {
            return ReturnBody.error("输入的金额不正确");
        }
        // 订单号
        String outTradeNo = UUIDUtil.getCode().toString();
        // 支付金额，单位：分
        BigDecimal totalAmount = BigDecimal.valueOf(money).multiply(BigDecimal.valueOf(100));
        // 回调路径
        String url = request.getRequestURL().toString();
        url = url.substring(0, url.lastIndexOf("/"));
        WxPayUnifiedOrderRequest payRequest;
        try {
            payRequest = WxPayUnifiedOrderRequest.newBuilder()
                    .outTradeNo(outTradeNo)
                    .body(body)
                    .totalFee(totalAmount.intValue())
                    .spbillCreateIp(InetAddress.getLocalHost().getHostAddress())
                    .tradeType("NATIVE")
                    .productId("10000")
                    .notifyUrl(url + "/public/notifyQrPay")
                    .attach(attach)
                    .build();
            WxPayNativeOrderResult order = this.wxPayService.createOrder(payRequest);
            return ReturnBody.success(order.getCodeUrl());
        } catch (UnknownHostException | WxPayException e) {
            e.printStackTrace();
            return ReturnBody.error("统一下单失败");
        }
    }


    @ApiIgnore
    @ApiOperation(value = "扫码支付回调通知处理")
    @PostMapping("/public/notifyQrPay")
    public String parseScanPayNotifyResult(String xmlData) throws WxPayException {
        final WxScanPayNotifyResult result = this.wxPayService.parseScanPayNotifyResult(xmlData);
        // TODO 业务逻辑处理，注意幂等性验证

        return WxPayNotifyResponse.success("成功");
    }

    public UsInfo checkUs(String orderNum) {
        Long usId = (Long) request.getAttribute("usId");
        if (CommUtil.checkNull(usId)) {
            return usInfoService.getByIdForUpdate(usId);
        } else {
            Long id = (Long) request.getAttribute("id");
            MerchantInfo merchantInfo = merchantInfoService.getByIdForUpdate(id);
            if (!CommUtil.checkNull(merchantInfo)) {
                throw new ServiceException("支付类型错误");
            }
            if (merchantInfo.getParentId() != -1) {
                MerchantInfo parentInfo = merchantInfoService.getOneForUpdate(new QueryWrapper<MerchantInfo>().eq(MerchantInfo.US_ID, merchantInfo.getParentId()));
                id = parentInfo.getId();
            }
            //查询当前用户是否和该商家账号入驻时的用户一致
            String[] split = orderNum.split(":");
            if (split.length != 2) {
                throw new ServiceException("支付参数错误");
            }
            UsInfo rawInfo = usInfoService.getByIdForUpdate(Long.parseLong(split[1]));
            UsInfo info = usInfoService.getOneForUpdate(new QueryWrapper<UsInfo>().eq(UsInfo.MERCHANT_ID, id));
            if (!CommUtil.checkNull(info) || !info.getId().equals(rawInfo.getId())) {
                throw new ServiceException("当前用户与商家不一致，请使用入驻时的微信号缴纳保证金");
            }
            return rawInfo;
        }
    }
}

